package com.ccbhelper.tranflows;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class JSONUtil implements JsonSupport{
	public static Object parse(String jsonStr){
		  return parse(new StringBuffer(jsonStr.trim()));
		 }
	private static Object parse(StringBuffer sb) {
		  try{
		   char c = sb.charAt(0);
		   if(c == ARR_START){
		    return parseArray(sb);
		   }else{
		    return parseObject(sb);
		   }
		  }catch(Exception e){
		   throw new java.lang.IllegalArgumentException("Invalid json string: " + sb.toString());
		  }
		 }
	 private static StringBuffer trim(StringBuffer sb){
		  while(sb.charAt(0) <= SPACE && sb.charAt(0) > Character.MIN_VALUE){
		   sb.deleteCharAt(0);
		  }
		  return sb;
		 }
	 private static Object processToken(StringBuffer sb, boolean isTokenKey){
		  if(sb.length() > 0){
		   int index = 0;
		   char startChar = sb.charAt(0);
		   Object value = null;
		   
		   // string token
		   if((startChar == D_QUOT) || (startChar == S_QUOT)){
		    boolean checkNextQuot = true;
		    while(++index < sb.length()){
		     if((sb.charAt(index) == startChar)){
		      if(checkNextQuot){
		       break;
		      }else{
		       checkNextQuot = true;
		      }
		     }else if(sb.charAt(index) == BACKSLASH){
		      checkNextQuot = !checkNextQuot;
		     }
		    }
		    value = sb.substring(1, index++);
		   // other value, number, null and other token directly split by comma
		   }else{
		    if(isTokenKey){
		     index = sb.indexOf(S_COLON);
		     value = sb.substring(0, index - 1).trim();
		    }else{
		     index = minNum(sb.indexOf(S_COMMA), sb.indexOf(S_OBJ_END), sb.indexOf(S_ARR_END));
		     value = detectValue(sb.substring(0, index).trim());
		    }
		   }
		   
		   sb.delete(0, index);
		   return value;
		  }
		  
		  return null;
		 }
	 private static int minNum(int... nums){
		  int res = Integer.MAX_VALUE;
		  for(int num : nums){
		   res = ((res < num) || (num == -1)) ? res : num;
		  }
		  return res;
		 }
	 private static Object detectValue(String objStr){
		  if(STATIC_VALUE_TRUE.equals(objStr) || STATIC_VALUE_FALSE.equals(objStr)){
		   return Boolean.valueOf(objStr);
		  }else if(STATIC_VALUE_NULL.equals(objStr) || STATIC_VALUE_UNDEFINED.equals(objStr)){
		   return null;
		  }else{
		   try{
		    return Byte.valueOf(objStr);
		   }catch(Exception e1){
		    if(objStr.length() == 0){
		     return Character.MIN_VALUE;
		    }else if(objStr.length() == 1){
		     return objStr.charAt(0);
		    }else{
		     try{
		      return Short.valueOf(objStr);
		     }catch(Exception e2){
		      try{
		       return Integer.valueOf(objStr);
		      }catch(Exception e3){
		       try{
		        return Long.valueOf(objStr);
		       }catch(Exception e4){
		        Float f = Float.valueOf(objStr);
		        if(f == Float.POSITIVE_INFINITY || f == Float.NEGATIVE_INFINITY){
		         return Double.valueOf(objStr);
		        }else{
		         return f;
		        }
		       }
		      }
		     }
		    }
		   }
		  }
		 }
	 private static Object parseNextValue(StringBuffer sb) throws Exception{
		  switch(sb.charAt(0)){
		   case OBJ_START:{
		    return parseObject(sb);
		   }
		   case ARR_START:{
		    return parseArray(sb);
		   }
		   case OBJ_END:{
		    return null;
		   }
		   case ARR_END:{
		    return null;
		   }
		   case COMMA:{
		    sb.deleteCharAt(0);
		    trim(sb);
		    return parseNextValue(sb);
		   }
		   default: {
		    return processToken(sb, false);
		   }
		  }
		 }
	 private static void parseNextValuePair(StringBuffer sb, Map oMap) throws Exception{
		  
		  if(sb.charAt(0) == COMMA){
		   sb.deleteCharAt(0);
		  }
		  
		  String key = (String)processToken(sb, true);
		  trim(sb);
		  sb.deleteCharAt(0);// delete the next colon
		  trim(sb);
		  
		  if(key.startsWith(COLLECTION_CLASS_PFRIEX)){
		   String val = (String)processToken(sb, false);
		   
		   sb.deleteCharAt(0);// delete the next comma
		   trim(sb);
		   
		   key = (String)processToken(sb, true);
		   trim(sb);
		   sb.deleteCharAt(0);// delete the next colon
		   trim(sb);
		   oMap.put(key, parseCollection(sb, (Collection) Class.forName(val).newInstance()));
		  }else if(key.startsWith(MAP_CLASS_PFRIEX)){
		   String val = (String)processToken(sb, false);
		   
		   sb.deleteCharAt(0);// delete the next comma
		   trim(sb);
		   
		   key = (String)processToken(sb, true);
		   trim(sb);
		   sb.deleteCharAt(0);// delete the next colon
		   trim(sb);
		   oMap.put(key, parseObject(sb, (Map)Class.forName(val).newInstance()));
		  }else{
		   oMap.put(key, parseNextValue(sb));
		  }
		 }
	 private static Object parseObject(StringBuffer sb) throws Exception{
		  return parseObject(sb, null);
		 }
	 @SuppressWarnings("unchecked")
	 private static Object parseObject(StringBuffer sb, Map oMap) throws Exception{
	  sb.deleteCharAt(0);
	  trim(sb);
	  if(oMap == null){
	   oMap = new HashMap();
	  }
	  
	  while(sb.charAt(0) != OBJ_END){
	   parseNextValuePair(sb, oMap);
	   trim(sb);
	  }
	  sb.deleteCharAt(0);
	  String className = (String)oMap.remove(CLASS_FIELD);
	  if(className != null){
	   Class c = Class.forName(className);
	   
	   Object obj = c.newInstance();
	   
	   if(Date.class.isInstance(obj)){
	    
	    Object timeVal = oMap.get(VALUE);
	    if(timeVal != null){
	     ((Date)obj).setTime((Long)timeVal);
	    }else{
	     obj = null;
	    }
	    
	   }else{
	    //String methodName = null;
	    for(Object fieldName : oMap.keySet()){
	     //methodName = METHOD_SET_PREFIX + ((String)fieldName).substring(0, 1).toUpperCase() + ((String)fieldName).substring(1);
	     //c.getDeclaredMethod(methodName).invoke(obj, new Object[]{oMap.get(fieldName)});
	     try{
	      Field f = c.getDeclaredField(String.valueOf(fieldName));
	      f.setAccessible(true);
	      f.set(obj, oMap.get(fieldName));
	     }catch(Exception e){
	      e.printStackTrace();
	      // skip any exception
	     }
	    }
	   }
	   
	   oMap.clear();
	   return obj;
	  }else{
	   return oMap;
	  }
	 }
	 public static Object[] parseArray(StringBuffer sb) throws Exception{
		  return parseCollection(sb, null).toArray();
		 }
	 @SuppressWarnings("unchecked")
	 public static Collection parseCollection(StringBuffer sb, Collection collection) throws Exception {
	  if(collection == null){
	   collection = new ArrayList();
	  }
	  
	  sb.deleteCharAt(0);
	  
	  while(sb.charAt(0) != ARR_END){
	   collection.add(parseNextValue(sb));
	   trim(sb);
	  }
	  
	  sb.deleteCharAt(0);
	  
	  return collection;
	 }
}
